﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace RobotMapper
{
    public static class Mapper
    {
        public static void Initialize(Action<RobotMapperCreator> action)
        {
            var robotMapperConfig = new RobotMapperCreator();
            GlobalConfig.IgnoreRoles.Clear();
            GlobalConfig.BindRoles.Clear();
            action?.Invoke(robotMapperConfig);
        }

        #region Mapper
        public static M RobotMap<T, M>(this T source)
            where T : class
            where M : class
        {
            return CreatMappers(source, Activator.CreateInstance<M>(), typeof(T), typeof(M), typeof(T));
        }

        public static List<M> RobotMap<T, M>(this List<T> source)
            where T : class
            where M : class
        {
            //return CreatMappers(source, Activator.CreateInstance<List<M>>(), typeof(List<T>), typeof(List<M>), new TypeKeySign());
            var result = new List<M>();
            source.ForEach(a =>
            {
                var newItem = CreatMappers(a, Activator.CreateInstance<M>(), typeof(T), typeof(M), typeof(T));
                result.Add(newItem);
            });
            return result;
        }

        private static M CreatMappers<T, M>(T source, M target, Type sType, Type dType, Type superior)
            where T : class
            where M : class
        {
            try
            {
                if (source == null) return null;
                //第一部分 需要找到自引用属性
                List<string> allIgnorePros = RobotMapperHelper.GetSelfRefType(dType).Select(a => a.PropertyName).ToList();
                //第二部分 需要找到循环引用属性 需要根据上级引用找到需忽略属性
                if (superior != dType)
                {
                    var allCircalIgnorePros = RobotMapperHelper.GetCircalRefType(superior, dType).Select(a => a.PropertyName).Distinct().ToList();
                    allIgnorePros.AddRange(allCircalIgnorePros);
                }
                allIgnorePros = allIgnorePros.Distinct().ToList();

                PropertyInfo[] sourceProperties = sType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                PropertyInfo[] destinationProperties = dType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (var destinationProperty in destinationProperties)
                {
                    //形成循环引用的属性 Ignore
                    if (allIgnorePros.Contains(destinationProperty.Name)) continue;

                    //指定规则需要忽略的 按照规则忽略
                    var ignoreRole = GlobalConfig.IgnoreRoles.FirstOrDefault(a =>
                      a.SourceType == typeof(T) &&
                      a.TargetType == typeof(M));
                    if (ignoreRole != null)
                    {
                        if (ignoreRole.IgnoreMembers.Contains(destinationProperty.Name))
                            continue;
                    }

                    //指定规则的 按照规则来
                    var role = GlobalConfig.BindRoles.FirstOrDefault(a =>
                        a.SourceType == typeof(T) &&
                        a.TargetType == typeof(M) &&
                        a.TargetPropertyName == destinationProperty.Name);
                    if (role != null)
                    {
                        var value = role.SourceFunc?.Invoke(source);
                        if (value == null) continue;
                        destinationProperty.SetValue(target, value, null);
                        continue;
                    }

                    //源中不存在 则Ignore
                    PropertyInfo sourceProperty = sourceProperties.FirstOrDefault(prop => RobotMapperHelper.NameMatches(prop.Name, destinationProperty.Name));
                    if (sourceProperty == null) continue;

                    //源不可读 则Ignore
                    if (!sourceProperty.CanRead) continue;

                    //目标不可写 则Ignore
                    if (!destinationProperty.CanWrite) continue;

                    Type sourcePropertyType = sourceProperty.PropertyType;
                    Type destinationPropertyType = destinationProperty.PropertyType;

                    //目标属性不可空(系统类型) 源属性如果是空值则忽略 不为空则赋值
                    if (!destinationPropertyType.IsGenericType && RobotMapperHelper.Filter(destinationPropertyType))
                    {
                        var value = sourceProperty.GetValue(source, null);
                        if (value == null) continue;
                        destinationProperty.SetValue(target, value, null);
                        continue;
                    }
                    
                    //目标属性为可空类型 直接给目标属性赋值
                    if (destinationPropertyType.IsGenericType && destinationPropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                    {
                        var value = sourceProperty.GetValue(source, null);
                        destinationProperty.SetValue(target, value, null);
                        continue;
                    }

                    //属性为泛型集合
                    if (destinationPropertyType.IsGenericType)
                    {
                        var destinationGenericType = destinationPropertyType.GetGenericArguments()[0];
                        var sourceGenericType = sourcePropertyType.GetGenericArguments()[0];
                        //系统对象直接赋值
                        if (RobotMapperHelper.Filter(destinationGenericType))
                            RobotMapperHelper.SetTargetValue(source, target, sourceProperty, destinationProperty);
                        //非系统对象则转化赋值
                        else
                        {
                            var value = sourceProperty.GetValue(source, null);
                            if (value == null) continue;
                            var sValue = value as IEnumerable;
                            //创建目标值集合
                            var dValue = Activator.CreateInstance(destinationPropertyType) as IList;
                            foreach (var item in sValue)
                            {
                                //创建目标对象
                                var dNewValue = Activator.CreateInstance(destinationGenericType);
                                //转换目标对象
                                dNewValue = CreatMappers(item, dNewValue, sourceGenericType, destinationGenericType, superior);
                                if (dNewValue == null) continue;
                                //将目标对象加入目标对象集合
                                dValue.Add(dNewValue);
                            }
                            destinationProperty.SetValue(target, dValue, null);
                        }
                    }
                    else
                    {
                        //系统对象直接赋值
                        if (RobotMapperHelper.Filter(destinationPropertyType))
                            RobotMapperHelper.SetTargetValue(source, target, sourceProperty, destinationProperty);
                        //非系统对象则转化赋值
                        else
                        {
                            var value = sourceProperty.GetValue(source, null);
                            if (value == null) continue;
                            var newO = Activator.CreateInstance(destinationPropertyType);
                            newO = CreatMappers(value, newO, sourcePropertyType, destinationPropertyType, superior);
                            destinationProperty.SetValue(target, newO, null);
                        }
                    }
                }

                return target;
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("在转化类型:{0}->{1}发生错误:{2}", sType, dType, ex.Message));
            }
        }
        #endregion
    }
}
